package APong.Entity;

import java.util.ArrayList;

import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.state.StateBasedGame;

import APong.World.World;

public abstract class Entity {
	
	//location
	protected float x,y;
	
	//velocity
	protected float xa,ya;
	
	//size
	protected int width,height;
	
	//world it exists in
	protected World world;
	
	//does it exist still in the world
	protected boolean removed;
	
	//decides if a object should keep his velocity or not
	protected boolean keepVelocity;
	
	//collision look
	protected boolean canPass;
	
	protected boolean useDefaultMove = true;
	//constructors---------------
	public Entity(World world){
		this.world = world;
	}
	public Entity(World world, float x, float y){
		this.world = world;
		this.x = x;
		this.y = y;
	}
	public Entity(World world, float x, float y, int w, int h){
		this.world = world;
		this.x = x;
		this.y = y;
		this.width = w;
		this.height = h;
	}
	
	//movement--------------------------------
	public void bounceX(){
		xa = -xa;
		
	}
	public void bounceY(){
		ya = -ya;
	}
	public void HandleCollision(Entity e){
		this.collide(e);
		e.collide(this);
	}
	public void move(float xa, float ya){
		if(xa != 0 && ya != 0){
			move(xa,0);
			move(0,ya);
			return;
		}
		canPass = true;
		ArrayList<Entity> entities = world.getEntities(this.getCollisionShape(xa, ya));
		for(Entity e: entities){
			if(e != this){
				if(e.isSolid(this) && this.isSolid(e)){
					HandleCollision(e);
				}
			}
		}
		if(canPass){
		x += world.getDeltaTime() * xa;
		y += world.getDeltaTime() * ya;
		}
	}
	public void moveTo(float xa, float ya, float speed){
		float xt,yt;
		if(this.x > xa){
			xt = -speed;
		}else
		if(this.x < xa){
			xt = speed;
		}else{
			xt = 0;
		}
		if(this.y > ya){
			yt = -speed;
		}else
		if(this.y < ya){
			yt = speed;
		}else{
			yt = 0;
		}
		canPass = true;
		ArrayList<Entity> entities = world.getEntities(this.getCollisionShape(xa, ya));
		for(Entity e: entities){
			if(e != this){
				if(e.isSolid(this) && this.isSolid(e)){
					HandleCollision(e);
				}
			}
		}
		if(canPass){
		x += world.getDeltaTime() * xt;
		y += world.getDeltaTime() * yt;
		}
	}
	
	//render Methods
	//draws the collisionBox------------------------------------
	public void drawCollisionShape(Graphics g){
		g.setColor(Color.white);
		g.draw(this.getCollisionShape(xa, ya));
	}
	//draws the collisionBox with custom color-----------------
	public void drawCollisionShape(Graphics g, Color color){
		g.setColor(color);
		g.draw(this.getCollisionShape(xa, ya));
	}
	//fills the collisionBox------------------------------------
		public void fillCollisionShape(Graphics g){
			g.setColor(Color.white);
			g.fill(this.getCollisionShape(xa, ya));
		}
		//fills the collisionBox with custom color-----------------
		public void fillCollisionShape(Graphics g, Color color){
			g.setColor(color);
			g.fill(this.getCollisionShape(xa, ya));
		}
	//normal render method-------------------------------------
	public void render(GameContainer gc, Graphics g){
		renderEntity(gc,g);
	}
	//update Methods
	public void update(GameContainer gc, StateBasedGame sbg){
		updateEntity(gc,sbg);
		if(useDefaultMove){
		move(xa,ya);
		if(!keepVelocity){
		this.setXVel(0);
		this.setYVel(0);
		}
		}
	}
	//abstract methods
	protected abstract void renderEntity(GameContainer gc, Graphics g);
	protected abstract void updateEntity(GameContainer gc, StateBasedGame sbg);
	public abstract Shape getCollisionShape(float xa, float ya);
	public abstract boolean isSolid(Entity e);
	public abstract void collide(Entity e);
	//setters-------------------------
	public void setXPos(float x){
		this.x = x;
		
	}
	public void setYPos(float y){
		this.y = y;
	}
	public void setXVel(float x){
		this.xa = x;
	}
	public void setYVel(float y){
		this.ya = y;
	}
	public void setRemoved(boolean b){
		this.removed = b;
	}
	public void setKeepVelocity(boolean b){
		this.keepVelocity = b;
	}
	public void setUseDefaultMove(boolean b){
		this.useDefaultMove = b;
	}
	//getters--------------------------------
	public float getXPos(){
		return x;
	}
	public float getYPos(){
		return y;
	}
	public float getXVel(){
		return xa;
	}
	public float getYVel(){
		return ya;
	}
	public boolean isRemoved(){
		return removed;
	}
	public World getWorld(){
		return world;
	}
	public boolean HasKeepVelocity(){
		return this.keepVelocity;
	}
	public boolean useDefaultMove(){
		return useDefaultMove;
	}
}
